home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / DDJMAG / DDJ9203.ZIP / GRAPHPRG.ASC < prev    next >
Text File  |  1992-01-24  |  30KB  |  691 lines

  1. _GRAPHICS PROGRAMMING COLUMN_
  2. by Michael Abrash
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. /* 3D animation program to rotate 12 cubes. Uses fixed point. All C code 
  8. tested with Borland C++ 3.0 in C compilation mode and the small model. */
  9. #include <conio.h>
  10. #include <dos.h>
  11. #include "polygon.h"
  12.  
  13. /* Base offset of page to which to draw */
  14. unsigned int CurrentPageBase = 0;
  15. /* Clip rectangle; clips to the screen */
  16. int ClipMinX = 0, ClipMinY = 0;
  17. int ClipMaxX = SCREEN_WIDTH, ClipMaxY = SCREEN_HEIGHT;
  18. static unsigned int PageStartOffsets[2] =
  19.    {PAGE0_START_OFFSET,PAGE1_START_OFFSET};
  20. int DisplayedPage, NonDisplayedPage;
  21. int RecalcAllXforms = 1, NumObjects = 0;
  22. Xform WorldViewXform;   /* initialized from floats */
  23. /* Pointers to objects */
  24. Object *ObjectList[MAX_OBJECTS];
  25.  
  26. void main() {
  27.    int Done = 0, i;
  28.    Object *ObjectPtr;
  29.    union REGS regset;
  30.  
  31.    InitializeFixedPoint(); /* set up fixed-point data */
  32.    InitializeCubes();    /* set up cubes and add them to object list; other 
  33.                          objects would be initialized now, if there were any */
  34.    Set320x240Mode(); /* set the screen to mode X */
  35.    ShowPage(PageStartOffsets[DisplayedPage = 0]);
  36.    /* Keep transforming the cube, drawing it to the undisplayed page,
  37.       and flipping the page to show it */
  38.    do {
  39.       /* For each object, regenerate viewing info, if necessary */
  40.       for (i=0; i<NumObjects; i++) {
  41.          if ((ObjectPtr = ObjectList[i])->RecalcXform ||
  42.                RecalcAllXforms) {
  43.             ObjectPtr->RecalcFunc(ObjectPtr);
  44.             ObjectPtr->RecalcXform = 0;
  45.          }
  46.       }
  47.       RecalcAllXforms = 0;
  48.       CurrentPageBase =    /* select other page for drawing to */
  49.             PageStartOffsets[NonDisplayedPage = DisplayedPage ^ 1];
  50.       /* For each object, clear the portion of the non-displayed page
  51.          that was drawn to last time, then reset the erase extent */
  52.       for (i=0; i<NumObjects; i++) {
  53.          ObjectPtr = ObjectList[i];
  54.          FillRectangleX(ObjectPtr->EraseRect[NonDisplayedPage].Left,
  55.             ObjectPtr->EraseRect[NonDisplayedPage].Top,
  56.             ObjectPtr->EraseRect[NonDisplayedPage].Right,
  57.             ObjectPtr->EraseRect[NonDisplayedPage].Bottom,
  58.             CurrentPageBase, 0);
  59.          ObjectPtr->EraseRect[NonDisplayedPage].Left =
  60.               ObjectPtr->EraseRect[NonDisplayedPage].Top = 0x7FFF;
  61.          ObjectPtr->EraseRect[NonDisplayedPage].Right =
  62.                ObjectPtr->EraseRect[NonDisplayedPage].Bottom = 0;
  63.       }
  64.       /* Draw all objects */
  65.       for (i=0; i<NumObjects; i++)
  66.          ObjectList[i]->DrawFunc(ObjectList[i]);
  67.       /* Flip to display the page into which we just drew */
  68.       ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
  69.       /* Move and reorient each object */
  70.       for (i=0; i<NumObjects; i++)
  71.          ObjectList[i]->MoveFunc(ObjectList[i]);
  72.       if (kbhit())
  73.          if (getch() == 0x1B) Done = 1;   /* Esc to exit */
  74.    } while (!Done);
  75.    /* Return to text mode and exit */
  76.    regset.x.ax = 0x0003;   /* AL = 3 selects 80x25 text mode */
  77.    int86(0x10, ®set, ®set);
  78.    exit(1);
  79. }
  80.  
  81.  
  82.  
  83. [LISTING TWO]
  84.  
  85. /* Transforms all vertices in the specified polygon-based object into view 
  86. space, then perspective projects them to screen space and maps them to screen 
  87. coordinates, storing results in the object. Recalculates object->view 
  88. transformation because only if transform changes would we bother 
  89. to retransform the vertices. */
  90. #include <math.h>
  91. #include "polygon.h"
  92.  
  93. void XformAndProjectPObject(PObject * ObjectToXform)
  94. {
  95.    int i, NumPoints = ObjectToXform->NumVerts;
  96.    Point3 * Points = ObjectToXform->VertexList;
  97.    Point3 * XformedPoints = ObjectToXform->XformedVertexList;
  98.    Point3 * ProjectedPoints = ObjectToXform->ProjectedVertexList;
  99.    Point * ScreenPoints = ObjectToXform->ScreenVertexList;
  100.  
  101.    /* Recalculate the object->view transform */
  102.    ConcatXforms(WorldViewXform, ObjectToXform->XformToWorld, 
  103.                                                  ObjectToXform->XformToView);
  104.    /* Apply that new transformation and project the points */
  105.    for (i=0; i<NumPoints; i++, Points++, XformedPoints++,
  106.          ProjectedPoints++, ScreenPoints++) {
  107.       /* Transform to view space */
  108.       XformVec(ObjectToXform->XformToView, (Fixedpoint *) Points,
  109.             (Fixedpoint *) XformedPoints);
  110.       /* Perspective-project to screen space */
  111.       ProjectedPoints->X =
  112.             FixedMul(FixedDiv(XformedPoints->X, XformedPoints->Z),
  113.             DOUBLE_TO_FIXED(PROJECTION_RATIO * (SCREEN_WIDTH/2)));
  114.       ProjectedPoints->Y =
  115.             FixedMul(FixedDiv(XformedPoints->Y, XformedPoints->Z),
  116.             DOUBLE_TO_FIXED(PROJECTION_RATIO * (SCREEN_WIDTH/2)));
  117.       ProjectedPoints->Z = XformedPoints->Z;
  118.       /* Convert to screen coordinates. The Y coord is negated to flip from 
  119.       increasing Y being up to increasing Y being down, as expected by polygon 
  120.       filler. Add in half the screen width and height to center on screen */
  121.       ScreenPoints->X = ((int) ((ProjectedPoints->X +
  122.             DOUBLE_TO_FIXED(0.5)) >> 16)) + SCREEN_WIDTH/2;
  123.       ScreenPoints->Y = (-((int) ((ProjectedPoints->Y +
  124.             DOUBLE_TO_FIXED(0.5)) >> 16))) + SCREEN_HEIGHT/2;
  125.    }
  126. }
  127.  
  128.  
  129. [LISTING THREE]
  130.  
  131. /* Routines to perform incremental rotations around the three axes. */
  132. #include <math.h>
  133. #include "polygon.h"
  134.  
  135. /* Concatenate a rotation by Angle around the X axis to transformation in 
  136.  XformToChange, placing the result back into XformToChange. */
  137. void AppendRotationX(Xform XformToChange, double Angle)
  138. {
  139.    Fixedpoint Temp10, Temp11, Temp12, Temp20, Temp21, Temp22;
  140.    Fixedpoint CosTemp = DOUBLE_TO_FIXED(cos(Angle));
  141.    Fixedpoint SinTemp = DOUBLE_TO_FIXED(sin(Angle));
  142.  
  143.    /* Calculate the new values of the six affected matrix entries */
  144.    Temp10 = FixedMul(CosTemp, XformToChange[1][0]) +
  145.          FixedMul(-SinTemp, XformToChange[2][0]);
  146.    Temp11 = FixedMul(CosTemp, XformToChange[1][1]) +
  147.          FixedMul(-SinTemp, XformToChange[2][1]);
  148.    Temp12 = FixedMul(CosTemp, XformToChange[1][2]) +
  149.          FixedMul(-SinTemp, XformToChange[2][2]);
  150.    Temp20 = FixedMul(SinTemp, XformToChange[1][0]) +
  151.          FixedMul(CosTemp, XformToChange[2][0]);
  152.    Temp21 = FixedMul(SinTemp, XformToChange[1][1]) +
  153.          FixedMul(CosTemp, XformToChange[2][1]);
  154.    Temp22 = FixedMul(SinTemp, XformToChange[1][2]) +
  155.          FixedMul(CosTemp, XformToChange[2][2]);
  156.    /* Put the results back into XformToChange */
  157.    XformToChange[1][0] = Temp10; XformToChange[1][1] = Temp11;
  158.    XformToChange[1][2] = Temp12; XformToChange[2][0] = Temp20; 
  159.    XformToChange[2][1] = Temp21; XformToChange[2][2] = Temp22;
  160. }
  161. /* Concatenate a rotation by Angle around the Y axis to transformation in 
  162.   XformToChange, placing the result back into XformToChange. */
  163. void AppendRotationY(Xform XformToChange, double Angle)
  164. {
  165.    Fixedpoint Temp00, Temp01, Temp02, Temp20, Temp21, Temp22;
  166.    Fixedpoint CosTemp = DOUBLE_TO_FIXED(cos(Angle));
  167.    Fixedpoint SinTemp = DOUBLE_TO_FIXED(sin(Angle));
  168.  
  169.    /* Calculate the new values of the six affected matrix entries */
  170.    Temp00 = FixedMul(CosTemp, XformToChange[0][0]) +
  171.          FixedMul(SinTemp, XformToChange[2][0]);
  172.    Temp01 = FixedMul(CosTemp, XformToChange[0][1]) +
  173.          FixedMul(SinTemp, XformToChange[2][1]);
  174.    Temp02 = FixedMul(CosTemp, XformToChange[0][2]) +
  175.          FixedMul(SinTemp, XformToChange[2][2]);
  176.    Temp20 = FixedMul(-SinTemp, XformToChange[0][0]) +
  177.          FixedMul( CosTemp, XformToChange[2][0]);
  178.    Temp21 = FixedMul(-SinTemp, XformToChange[0][1]) +
  179.          FixedMul(CosTemp, XformToChange[2][1]);
  180.    Temp22 = FixedMul(-SinTemp, XformToChange[0][2]) +
  181.          FixedMul(CosTemp, XformToChange[2][2]);
  182.    /* Put the results back into XformToChange */
  183.    XformToChange[0][0] = Temp00; XformToChange[0][1] = Temp01;
  184.    XformToChange[0][2] = Temp02; XformToChange[2][0] = Temp20;
  185.    XformToChange[2][1] = Temp21; XformToChange[2][2] = Temp22;
  186. }
  187.  
  188. /* Concatenate a rotation by Angle around the Z axis to transformation in 
  189.   XformToChange, placing the result back into XformToChange. */
  190. void AppendRotationZ(Xform XformToChange, double Angle)
  191. {
  192.    Fixedpoint Temp00, Temp01, Temp02, Temp10, Temp11, Temp12;
  193.    Fixedpoint CosTemp = DOUBLE_TO_FIXED(cos(Angle));
  194.    Fixedpoint SinTemp = DOUBLE_TO_FIXED(sin(Angle));
  195.  
  196.    /* Calculate the new values of the six affected matrix entries */
  197.    Temp00 = FixedMul(CosTemp, XformToChange[0][0]) +
  198.          FixedMul(-SinTemp, XformToChange[1][0]);
  199.    Temp01 = FixedMul(CosTemp, XformToChange[0][1]) +
  200.          FixedMul(-SinTemp, XformToChange[1][1]);
  201.    Temp02 = FixedMul(CosTemp, XformToChange[0][2]) +
  202.          FixedMul(-SinTemp, XformToChange[1][2]);
  203.    Temp10 = FixedMul(SinTemp, XformToChange[0][0]) +
  204.          FixedMul(CosTemp, XformToChange[1][0]);
  205.    Temp11 = FixedMul(SinTemp, XformToChange[0][1]) +
  206.          FixedMul(CosTemp, XformToChange[1][1]);
  207.    Temp12 = FixedMul(SinTemp, XformToChange[0][2]) +
  208.          FixedMul(CosTemp, XformToChange[1][2]);
  209.    /* Put the results back into XformToChange */
  210.    XformToChange[0][0] = Temp00; XformToChange[0][1] = Temp01;
  211.    XformToChange[0][2] = Temp02; XformToChange[1][0] = Temp10;
  212.    XformToChange[1][1] = Temp11; XformToChange[1][2] = Temp12;
  213. }
  214.  
  215.  
  216.  
  217. [LISTING FOUR]
  218.  
  219. /* Fixed point matrix arithmetic functions */
  220. #include "polygon.h"
  221.  
  222. /* Matrix multiplies Xform by SourceVec, and stores the result in DestVec. 
  223. Multiplies a 4x4 matrix times a 4x1 matrix; the result is a 4x1 matrix. Cheats 
  224. by assuming the W coord is 1 and bottom row of matrix is 0 0 0 1, and doesn't 
  225. bother to set the W coordinate of the destination */
  226. void XformVec(Xform WorkingXform, Fixedpoint *SourceVec,
  227.    Fixedpoint *DestVec)
  228. {
  229.    int i;
  230.  
  231.    for (i=0; i<3; i++)
  232.       DestVec[i] = FixedMul(WorkingXform[i][0], SourceVec[0]) +
  233.             FixedMul(WorkingXform[i][1], SourceVec[1]) +
  234.             FixedMul(WorkingXform[i][2], SourceVec[2]) +
  235.             WorkingXform[i][3];   /* no need to multiply by W = 1 */
  236. }
  237.  
  238. /* Matrix multiplies SourceXform1 by SourceXform2 and stores result in 
  239.  DestXform. Multiplies a 4x4 matrix times a 4x4 matrix; result is a 4x4 matrix.
  240.  Cheats by assuming bottom row of each matrix is 0 0 0 1, and doesn't bother 
  241.  to set the bottom row of the destination */
  242. void ConcatXforms(Xform SourceXform1, Xform SourceXform2,
  243.    Xform DestXform)
  244. {
  245.    int i, j;
  246.  
  247.    for (i=0; i<3; i++) {
  248.       for (j=0; j<4; j++)
  249.          DestXform[i][j] =
  250.                FixedMul(SourceXform1[i][0], SourceXform2[0][j]) +
  251.                FixedMul(SourceXform1[i][1], SourceXform2[1][j]) +
  252.                FixedMul(SourceXform1[i][2], SourceXform2[2][j]) +
  253.                SourceXform1[i][3];
  254.    }
  255. }
  256.  
  257.  
  258.  
  259. [LISTING FIVE]
  260.  
  261. /* Set up basic data that needs to be in fixed point, to avoid data
  262.    definition hassles. */
  263. #include "polygon.h"
  264.  
  265. /* All vertices in the basic cube */
  266. static IntPoint3 IntCubeVerts[NUM_CUBE_VERTS] = {
  267.    {15,15,15},{15,15,-15},{15,-15,15},{15,-15,-15},
  268.    {-15,15,15},{-15,15,-15},{-15,-15,15},{-15,-15,-15} };
  269. /* Transformation from world space into view space (no transformation,
  270.    currently) */
  271. static int IntWorldViewXform[3][4] = {
  272.    {1,0,0,0}, {0,1,0,0}, {0,0,1,0}};
  273.  
  274. void InitializeFixedPoint()
  275. {
  276.    int i, j;
  277.  
  278.    for (i=0; i<3; i++)
  279.       for (j=0; j<4; j++)
  280.          WorldViewXform[i][j] = INT_TO_FIXED(IntWorldViewXform[i][j]);
  281.    for (i=0; i<NUM_CUBE_VERTS; i++) {
  282.       CubeVerts[i].X = INT_TO_FIXED(IntCubeVerts[i].X);
  283.       CubeVerts[i].Y = INT_TO_FIXED(IntCubeVerts[i].Y);
  284.       CubeVerts[i].Z = INT_TO_FIXED(IntCubeVerts[i].Z);
  285.    }
  286. }
  287.  
  288.  
  289. [LISTING SIX]
  290.  
  291. /* Rotates and moves a polygon-based object around the three axes.
  292.    Movement is implemented only along the Z axis currently. */
  293. #include "polygon.h"
  294.  
  295. void RotateAndMovePObject(PObject * ObjectToMove)
  296. {
  297.    if (--ObjectToMove->RDelayCount == 0) {   /* rotate */
  298.       ObjectToMove->RDelayCount = ObjectToMove->RDelayCountBase;
  299.       if (ObjectToMove->Rotate.RotateX != 0.0)
  300.          AppendRotationX(ObjectToMove->XformToWorld,
  301.                ObjectToMove->Rotate.RotateX);
  302.       if (ObjectToMove->Rotate.RotateY != 0.0)
  303.          AppendRotationY(ObjectToMove->XformToWorld,
  304.                ObjectToMove->Rotate.RotateY);
  305.       if (ObjectToMove->Rotate.RotateZ != 0.0)
  306.          AppendRotationZ(ObjectToMove->XformToWorld,
  307.                ObjectToMove->Rotate.RotateZ);
  308.       ObjectToMove->RecalcXform = 1;
  309.    }
  310.    /* Move in Z, checking for bouncing and stopping */
  311.    if (--ObjectToMove->MDelayCount == 0) {
  312.       ObjectToMove->MDelayCount = ObjectToMove->MDelayCountBase;
  313.       ObjectToMove->XformToWorld[2][3] += ObjectToMove->Move.MoveZ;
  314.       if (ObjectToMove->XformToWorld[2][3]>ObjectToMove->Move.MaxZ)
  315.          ObjectToMove->Move.MoveZ = 0; /* stop if close enough */
  316.       ObjectToMove->RecalcXform = 1;
  317.    }
  318. }
  319.  
  320.  
  321.  
  322. [LISTING SEVEN]
  323.  
  324. /* Draws all visible faces in specified polygon-based object. Object must have 
  325. previously been transformed and projected, so that ScreenVertexList array is 
  326. filled in. */
  327. #include "polygon.h"
  328.  
  329. void DrawPObject(PObject * ObjectToXform)
  330. {
  331.    int i, j, NumFaces = ObjectToXform->NumFaces, NumVertices;
  332.    int * VertNumsPtr;
  333.    Face * FacePtr = ObjectToXform->FaceList;
  334.    Point * ScreenPoints = ObjectToXform->ScreenVertexList;
  335.    long v1, v2, w1, w2;
  336.    Point Vertices[MAX_POLY_LENGTH];
  337.    PointListHeader Polygon;
  338.  
  339.    /* Draw each visible face (polygon) of the object in turn */
  340.    for (i=0; i<NumFaces; i++, FacePtr++) {
  341.       NumVertices = FacePtr->NumVerts;
  342.       /* Copy over the face's vertices from the vertex list */
  343.       for (j=0, VertNumsPtr=FacePtr->VertNums; j<NumVertices; j++)
  344.          Vertices[j] = ScreenPoints[*VertNumsPtr++];
  345.       /* Draw only if outside face showing (if the normal to the
  346.          polygon points toward viewer; that is, has a positive Z component) */
  347.       v1 = Vertices[1].X - Vertices[0].X;
  348.       w1 = Vertices[NumVertices-1].X - Vertices[0].X;
  349.       v2 = Vertices[1].Y - Vertices[0].Y;
  350.       w2 = Vertices[NumVertices-1].Y - Vertices[0].Y;
  351.       if ((v1*w2 - v2*w1) > 0) {
  352.          /* It is facing the screen, so draw */
  353.          /* Appropriately adjust the extent of the rectangle used to
  354.             erase this object later */
  355.          for (j=0; j<NumVertices; j++) {
  356.             if (Vertices[j].X >
  357.                   ObjectToXform->EraseRect[NonDisplayedPage].Right)
  358.                if (Vertices[j].X < SCREEN_WIDTH)
  359.                   ObjectToXform->EraseRect[NonDisplayedPage].Right =
  360.                         Vertices[j].X;
  361.                else ObjectToXform->EraseRect[NonDisplayedPage].Right =
  362.                      SCREEN_WIDTH;
  363.             if (Vertices[j].Y >
  364.                   ObjectToXform->EraseRect[NonDisplayedPage].Bottom)
  365.                if (Vertices[j].Y < SCREEN_HEIGHT)
  366.                   ObjectToXform->EraseRect[NonDisplayedPage].Bottom =
  367.                         Vertices[j].Y;
  368.                else ObjectToXform->EraseRect[NonDisplayedPage].Bottom=
  369.                      SCREEN_HEIGHT;
  370.             if (Vertices[j].X <
  371.                   ObjectToXform->EraseRect[NonDisplayedPage].Left)
  372.                if (Vertices[j].X > 0)
  373.                   ObjectToXform->EraseRect[NonDisplayedPage].Left =
  374.                         Vertices[j].X;
  375.                else ObjectToXform->EraseRect[NonDisplayedPage].Left=0;
  376.             if (Vertices[j].Y <
  377.                   ObjectToXform->EraseRect[NonDisplayedPage].Top)
  378.                if (Vertices[j].Y > 0)
  379.                   ObjectToXform->EraseRect[NonDisplayedPage].Top =
  380.                         Vertices[j].Y;
  381.                else ObjectToXform->EraseRect[NonDisplayedPage].Top=0;
  382.          }
  383.          /* Draw the polygon */
  384.          DRAW_POLYGON(Vertices, NumVertices, FacePtr->Color, 0, 0);
  385.       }
  386.    }
  387. }
  388.  
  389.  
  390.  
  391. [LISTING EIGHT]
  392.  
  393. /* Initializes the cubes and adds them to the object list. */
  394. #include <stdlib.h>
  395. #include <math.h>
  396. #include "polygon.h"
  397.  
  398. #define ROT_6  (M_PI / 30.0)     /* rotate 6 degrees at a time */
  399. #define ROT_3  (M_PI / 60.0)     /* rotate 3 degrees at a time */
  400. #define ROT_2  (M_PI / 90.0)     /* rotate 2 degrees at a time */
  401. #define NUM_CUBES 12             /* # of cubes */
  402. Point3 CubeVerts[NUM_CUBE_VERTS]; /* set elsewhere, from floats */
  403. /* Vertex indices for individual cube faces */
  404. static int Face1[] = {1,3,2,0};
  405. static int Face2[] = {5,7,3,1};
  406. static int Face3[] = {4,5,1,0};
  407. static int Face4[] = {3,7,6,2};
  408. static int Face5[] = {5,4,6,7};
  409. static int Face6[] = {0,2,6,4};
  410. static int *VertNumList[]={Face1, Face2, Face3, Face4, Face5, Face6};
  411. static int VertsInFace[]={ sizeof(Face1)/sizeof(int),
  412.    sizeof(Face2)/sizeof(int), sizeof(Face3)/sizeof(int),
  413.    sizeof(Face4)/sizeof(int), sizeof(Face5)/sizeof(int),
  414.    sizeof(Face6)/sizeof(int) };
  415. /* X, Y, Z rotations for cubes */
  416. static RotateControl InitialRotate[NUM_CUBES] = {
  417.    {0.0,ROT_6,ROT_6},{ROT_3,0.0,ROT_3},{ROT_3,ROT_3,0.0},
  418.    {ROT_3,-ROT_3,0.0},{-ROT_3,ROT_2,0.0},{-ROT_6,-ROT_3,0.0},
  419.    {ROT_3,0.0,-ROT_6},{-ROT_2,0.0,ROT_3},{-ROT_3,0.0,-ROT_3},
  420.    {0.0,ROT_2,-ROT_2},{0.0,-ROT_3,ROT_3},{0.0,-ROT_6,-ROT_6},};
  421. static MoveControl InitialMove[NUM_CUBES] = {
  422.    {0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
  423.    {0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
  424.    {0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
  425.    {0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
  426.    {0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
  427.    {0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350}, };
  428. /* Face colors for various cubes */
  429. static int Colors[NUM_CUBES][NUM_CUBE_FACES] = {
  430.    {15,14,12,11,10,9},{1,2,3,4,5,6},{35,37,39,41,43,45},
  431.    {47,49,51,53,55,57},{59,61,63,65,67,69},{71,73,75,77,79,81},
  432.    {83,85,87,89,91,93},{95,97,99,101,103,105},
  433.    {107,109,111,113,115,117},{119,121,123,125,127,129},
  434.    {131,133,135,137,139,141},{143,145,147,149,151,153} };
  435. /* Starting coordinates for cubes in world space */
  436. static int CubeStartCoords[NUM_CUBES][3] = {
  437.    {100,0,-6000},  {100,70,-6000}, {100,-70,-6000}, {33,0,-6000},
  438.    {33,70,-6000},  {33,-70,-6000}, {-33,0,-6000},   {-33,70,-6000},
  439.    {-33,-70,-6000},{-100,0,-6000}, {-100,70,-6000}, {-100,-70,-6000}};
  440. /* Delay counts (speed control) for cubes */
  441. static int InitRDelayCounts[NUM_CUBES] = {1,2,1,2,1,1,1,1,1,2,1,1};
  442. static int BaseRDelayCounts[NUM_CUBES] = {1,2,1,2,2,1,1,1,2,2,2,1};
  443. static int InitMDelayCounts[NUM_CUBES] = {1,1,1,1,1,1,1,1,1,1,1,1};
  444. static int BaseMDelayCounts[NUM_CUBES] = {1,1,1,1,1,1,1,1,1,1,1,1};
  445.  
  446. void InitializeCubes()
  447. {
  448.    int i, j, k;
  449.    PObject *WorkingCube;
  450.  
  451.    for (i=0; i<NUM_CUBES; i++) {
  452.       if ((WorkingCube = malloc(sizeof(PObject))) == NULL) {
  453.          printf("Couldn't get memory\n"); exit(1); }
  454.       WorkingCube->DrawFunc = DrawPObject;
  455.       WorkingCube->RecalcFunc = XformAndProjectPObject;
  456.       WorkingCube->MoveFunc = RotateAndMovePObject;
  457.       WorkingCube->RecalcXform = 1;
  458.       for (k=0; k<2; k++) {
  459.          WorkingCube->EraseRect[k].Left =
  460.             WorkingCube->EraseRect[k].Top = 0x7FFF;
  461.          WorkingCube->EraseRect[k].Right = 0;
  462.          WorkingCube->EraseRect[k].Bottom = 0;
  463.       }
  464.       WorkingCube->RDelayCount = InitRDelayCounts[i];
  465.       WorkingCube->RDelayCountBase = BaseRDelayCounts[i];
  466.       WorkingCube->MDelayCount = InitMDelayCounts[i];
  467.       WorkingCube->MDelayCountBase = BaseMDelayCounts[i];
  468.       /* Set the object->world xform to none */
  469.       for (j=0; j<3; j++)
  470.          for (k=0; k<4; k++)
  471.             WorkingCube->XformToWorld[j][k] = INT_TO_FIXED(0);
  472.       WorkingCube->XformToWorld[0][0] = 
  473.          WorkingCube->XformToWorld[1][1] =
  474.          WorkingCube->XformToWorld[2][2] =
  475.          WorkingCube->XformToWorld[3][3] = INT_TO_FIXED(1);
  476.       /* Set the initial location */
  477.       for (j=0; j<3; j++) WorkingCube->XformToWorld[j][3] =
  478.             INT_TO_FIXED(CubeStartCoords[i][j]);
  479.       WorkingCube->NumVerts = NUM_CUBE_VERTS;
  480.       WorkingCube->VertexList = CubeVerts;
  481.       WorkingCube->NumFaces = NUM_CUBE_FACES;
  482.       WorkingCube->Rotate = InitialRotate[i];
  483.       WorkingCube->Move.MoveX = INT_TO_FIXED(InitialMove[i].MoveX);
  484.       WorkingCube->Move.MoveY = INT_TO_FIXED(InitialMove[i].MoveY);
  485.       WorkingCube->Move.MoveZ = INT_TO_FIXED(InitialMove[i].MoveZ);
  486.       WorkingCube->Move.MinX = INT_TO_FIXED(InitialMove[i].MinX);
  487.       WorkingCube->Move.MinY = INT_TO_FIXED(InitialMove[i].MinY);
  488.       WorkingCube->Move.MinZ = INT_TO_FIXED(InitialMove[i].MinZ);
  489.       WorkingCube->Move.MaxX = INT_TO_FIXED(InitialMove[i].MaxX);
  490.       WorkingCube->Move.MaxY = INT_TO_FIXED(InitialMove[i].MaxY);
  491.       WorkingCube->Move.MaxZ = INT_TO_FIXED(InitialMove[i].MaxZ);
  492.       if ((WorkingCube->XformedVertexList =
  493.             malloc(NUM_CUBE_VERTS*sizeof(Point3))) == NULL) {
  494.          printf("Couldn't get memory\n"); exit(1); }
  495.       if ((WorkingCube->ProjectedVertexList =
  496.             malloc(NUM_CUBE_VERTS*sizeof(Point3))) == NULL) {
  497.          printf("Couldn't get memory\n"); exit(1); }
  498.       if ((WorkingCube->ScreenVertexList =
  499.             malloc(NUM_CUBE_VERTS*sizeof(Point))) == NULL) {
  500.          printf("Couldn't get memory\n"); exit(1); }
  501.       if ((WorkingCube->FaceList =
  502.             malloc(NUM_CUBE_FACES*sizeof(Face))) == NULL) {
  503.          printf("Couldn't get memory\n"); exit(1); }
  504.       /* Initialize the faces */
  505.       for (j=0; j<NUM_CUBE_FACES; j++) {
  506.          WorkingCube->FaceList[j].VertNums = VertNumList[j];
  507.          WorkingCube->FaceList[j].NumVerts = VertsInFace[j];
  508.          WorkingCube->FaceList[j].Color = Colors[i][j];
  509.       }
  510.       ObjectList[NumObjects++] = (Object *)WorkingCube;
  511.    }
  512. }
  513.  
  514.  
  515.  
  516. [LISTING NINE]
  517.  
  518. ; 386-specific fixed point multiply and divide.
  519. ; C near-callable as: Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
  520. ;                 Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
  521. ; Tested with TASM 3.0.
  522.         .model small
  523.         .386
  524.         .code
  525.         public  _FixedMul,_FixedDiv
  526. ; Multiplies two fixed-point values together.
  527. FMparms struc
  528.         dw      2 dup(?)        ;return address & pushed BP
  529. M1      dd      ?
  530. M2      dd      ?
  531. FMparms ends
  532.         align   2
  533. _FixedMul       proc    near
  534.         push    bp
  535.         mov     bp,sp
  536.         mov     eax,[bp+M1]
  537.         imul    dword ptr [bp+M2] ;multiply
  538.         add     eax,8000h       ;round by adding 2^(-16)
  539.         adc     edx,0           ;whole part of result is in DX
  540.         shr     eax,16          ;put the fractional part in AX
  541.         pop     bp
  542.         ret
  543. _FixedMul       endp
  544. ; Divides one fixed-point value by another.
  545. FDparms struc
  546.         dw      2 dup(?)        ;return address & pushed BP
  547. Dividend dd     ?
  548. Divisor  dd     ?
  549. FDparms ends
  550.         align   2
  551. _FixedDiv       proc    near
  552.         push    bp
  553.         mov     bp,sp
  554.         sub     cx,cx           ;assume positive result
  555.         mov     eax,[bp+Dividend]
  556.         and     eax,eax         ;positive dividend?
  557.         jns     FDP1            ;yes
  558.         inc     cx              ;mark it's a negative dividend
  559.         neg     eax             ;make the dividend positive
  560. FDP1:   sub     edx,edx         ;make it a 64-bit dividend, then shift
  561.                                 ; left 16 bits so that result will be in EAX
  562.         rol     eax,16          ;put fractional part of dividend in
  563.                                 ; high word of EAX
  564.         mov     dx,ax           ;put whole part of dividend in DX
  565.         sub     ax,ax           ;clear low word of EAX
  566.         mov     ebx,dword ptr [bp+Divisor]
  567.         and     ebx,ebx         ;positive divisor?
  568.         jns     FDP2            ;yes
  569.         dec     cx              ;mark it's a negative divisor
  570.         neg     ebx             ;make divisor positive
  571. FDP2:   div     ebx             ;divide
  572.         shr     ebx,1           ;divisor/2, minus 1 if the divisor is
  573.         adc     ebx,0           ; even
  574.         dec     ebx
  575.         cmp     ebx,edx         ;set Carry if remainder is at least
  576.         adc     eax,0           ; half as large as the divisor, then
  577.                                 ; use that to round up if necessary
  578.         and     cx,cx           ;should the result be made negative?
  579.         jz      FDP3            ;no
  580.         neg     eax             ;yes, negate it
  581. FDP3:   mov     edx,eax         ;return result in DX:AX; fractional
  582.                                 ; part is already in AX
  583.         shr     edx,16          ;whole part of result in DX
  584.         pop     bp
  585.         ret
  586. _FixedDiv       endp
  587.         end
  588.  
  589.  
  590.  
  591. [LISTING TEN]
  592.  
  593. /* POLYGON.H: Header file for polygon-filling code, also includes
  594.    a number of useful items for 3D animation. */
  595. #define MAX_OBJECTS  100   /* max simultaneous # objects supported */
  596. #define MAX_POLY_LENGTH 4  /* four vertices is the max per poly */
  597. #define SCREEN_WIDTH 320
  598. #define SCREEN_HEIGHT 240
  599. #define PAGE0_START_OFFSET 0
  600. #define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
  601. #define NUM_CUBE_VERTS 8              /* # of vertices per cube */
  602. #define NUM_CUBE_FACES 6              /* # of faces per cube */
  603. /* Ratio: distance from viewpoint to projection plane / width of
  604.    projection plane. Defines the width of the field of view. Lower
  605.    absolute values = wider fields of view; higher values = narrower */
  606. #define PROJECTION_RATIO -2.0 /* negative because visible Z
  607.                                  coordinates are negative */
  608. /* Draws the polygon described by the point list PointList in color
  609.    Color with all vertices offset by (X,Y) */
  610. #define DRAW_POLYGON(PointList,NumPoints,Color,X,Y)          \
  611.    Polygon.Length = NumPoints; Polygon.PointPtr = PointList; \
  612.    FillConvexPolygon(&Polygon, Color, X, Y);
  613. #define INT_TO_FIXED(x) (((long)(int)x) << 16)
  614. #define DOUBLE_TO_FIXED(x) ((long) (x * 65536.0 + 0.5))
  615.  
  616. typedef long Fixedpoint;
  617. typedef Fixedpoint Xform[3][4];
  618. /* Describes a single 2D point */
  619. typedef struct { int X; int Y; } Point;
  620. /* Describes a single 3D point in homogeneous coordinates; the W
  621.    coordinate isn't present, though; assumed to be 1 and implied */
  622. typedef struct { Fixedpoint X, Y, Z; } Point3;
  623. typedef struct { int X; int Y; int Z; } IntPoint3;
  624. /* Describes a series of points (used to store a list of vertices that
  625.    describe a polygon; each vertex is assumed to connect to the two
  626.    adjacent vertices; last vertex is assumed to connect to first) */
  627. typedef struct { int Length; Point * PointPtr; } PointListHeader;
  628. /* Describes the beginning and ending X coordinates of a single
  629.    horizontal line */
  630. typedef struct { int XStart; int XEnd; } HLine;
  631. /* Describes a Length-long series of horizontal lines, all assumed to
  632.    be on contiguous scan lines starting at YStart and proceeding
  633.    downward (used to describe a scan-converted polygon to the
  634.    low-level hardware-dependent drawing code) */
  635. typedef struct { int Length; int YStart; HLine * HLinePtr;} HLineList;
  636. typedef struct { int Left, Top, Right, Bottom; } Rect;
  637. /* Structure describing one face of an object (one polygon) */
  638. typedef struct { int * VertNums; int NumVerts; int Color; }  Face;
  639. typedef struct { double RotateX, RotateY, RotateZ; } RotateControl;
  640. typedef struct { Fixedpoint MoveX, MoveY, MoveZ, MinX, MinY, MinZ,
  641.    MaxX, MaxY, MaxZ; } MoveControl;
  642. /* Fields common to every object */
  643. #define BASE_OBJECT                                              \
  644.    void (*DrawFunc)();     /* draws object */                    \
  645.    void (*RecalcFunc)();   /* prepares object for drawing */     \
  646.    void (*MoveFunc)();     /* moves object */                    \
  647.    int RecalcXform;        /* 1 to indicate need to recalc */    \
  648.    Rect EraseRect[2];      /* rectangle to erase in each page */
  649. /* Basic object */
  650. typedef struct { BASE_OBJECT } Object;
  651. /* Structure describing a polygon-based object */
  652. typedef struct {
  653.    BASE_OBJECT
  654.    int RDelayCount, RDelayCountBase; /* controls rotation speed */
  655.    int MDelayCount, MDelayCountBase; /* controls movement speed */
  656.    Xform XformToWorld;        /* transform from object->world space */
  657.    Xform XformToView;         /* transform from object->view space */
  658.    RotateControl Rotate;      /* controls rotation change over time */
  659.    MoveControl Move;          /* controls object movement over time */
  660.    int NumVerts;              /* # vertices in VertexList */
  661.    Point3 * VertexList;       /* untransformed vertices */
  662.    Point3 * XformedVertexList;   /* transformed into view space */
  663.    Point3 * ProjectedVertexList; /* projected into screen space */
  664.    Point * ScreenVertexList;     /* converted to screen coordinates */
  665.    int NumFaces;              /* # of faces in object */
  666.    Face * FaceList;           /* pointer to face info */
  667. } PObject;
  668.  
  669. extern void XformVec(Xform, Fixedpoint *, Fixedpoint *);
  670. extern void ConcatXforms(Xform, Xform, Xform);
  671. extern int FillConvexPolygon(PointListHeader *, int, int, int);
  672. extern void Set320x240Mode(void);
  673. extern void ShowPage(unsigned int);
  674. extern void FillRectangleX(int, int, int, int, unsigned int, int);
  675. extern void XformAndProjectPObject(PObject *);
  676. extern void DrawPObject(PObject *);
  677. extern void AppendRotationX(Xform, double);
  678. extern void AppendRotationY(Xform, double);
  679. extern void AppendRotationZ(Xform, double);
  680. extern near Fixedpoint FixedMul(Fixedpoint, Fixedpoint);
  681. extern near Fixedpoint FixedDiv(Fixedpoint, Fixedpoint);
  682. extern void InitializeFixedPoint(void);
  683. extern void RotateAndMovePObject(PObject *);
  684. extern void InitializeCubes(void);
  685. extern int DisplayedPage, NonDisplayedPage, RecalcAllXforms;
  686. extern int NumObjects;
  687. extern Xform WorldViewXform;
  688. extern Object *ObjectList[];
  689. extern Point3 CubeVerts[];
  690.  
  691.